#ifndef COMPONENTS_HPP_
#define COMPONENTS_HPP_
namespace quadil{

/**
 * A class that implements the notion of a LABELED component.
 * A labeled component is an object consisting of all the pixels
 * in a Image<t_label> that are equal to any particular label
 * except the special label "0". This class will keep a reference
 * to the image data of the original image.
 * Labeled components are a very usefull for feature extraction.
 */
class Component{
#ifdef QUADIL_TUNE_COMPONENT_MEMORY_SAFETY
	Image<t_label> img;
#else
	Image<t_label>& img;
#endif
	const t_label label;
	int nbPixels;
	int left;
	int right;
	int top;
	int bottom;
	long int xSum;
	long int ySum;
	Component(Image<t_label>& i,int x,int y,t_label lab):img(i),label(lab){
		left=x;
		right=x;
		top=y;
		bottom=y;
		nbPixels=1;
		xSum=x;
		ySum=y;
	}
	void addPixel(int x, int y){
		(x<left)&&(left=x);
		(x>right)&&(right=x);
		(y<top)&&(top=y);
		(y>bottom)&&(bottom=y);
		nbPixels++;
		xSum+=x;
		ySum+=y;
	}
	friend class ComponentSet;
public:
	void setAllPixelsToLabel(t_label l){
		int x,y,lineStride;
		t_label* curRow;
		for(y=top;y<=bottom;y++){
			curRow=img.getRow(y);
			for(x=left;x<=right;x++){
				(curRow[x]==label)&&(curRow[x]=l);
			}
		}
		if(l!=label){
			left=0;
			right=0;
			top=0;
			bottom=0;
			nbPixels=0;
			xSum=0;
			ySum=0;
		}
	}
	template <typename T>void drawOnImage(Image<T>& i,T val)const{
#ifdef QUADIL_TUNE_CHECK_SAME_SIZE_IMAGES
		if(i.height!=img.height || i.width!=i.width){
			throw "quadil::Component::drawOnImage Image i, should have the same size as the image containing the component";
		}
#endif
		int x,y,lineStride;
		const t_label* curRowIn;
		T* curRowOut;
		for(y=top;y<=bottom;y++){
			curRowIn=img.getRow(y);
			curRowOut=i.getRow(y);
			for(x=left;x<=right;x++){
				(curRowIn[x]==label)&&(curRowOut[x]=val);
			}
		}
	}
	Image<t_byte> getAsByteImage()const{
		Image<t_byte> res(this->getWidth(),this->getHeight());
		t_byte* outCurLine;
		const t_label* inCurLine;
		int x,y;
		outCurLine=res.getRow(0);
		inCurLine=img.getRow(this->top)+this->left;
		for(y=0;y<this->getHeight();y++){
			for(x=this->getWidth();x>=0;x--){
				outCurLine[x]=(inCurLine[x]==label)?0:255;
			}
		}
		return res;
	}
	double getCenterX()const{
		return xSum/nbPixels;
	}
	double getCenterY()const{
		return ySum/nbPixels;
	}
	int getLeft()const{
		return left;
	}
	int getRight()const{
		return right;
	}
	int getTop()const{
		return top;
	}
	int getBottom()const{
		return bottom;
	}
	int getNbPixels()const{
		return nbPixels;
	}
	int getWidth()const{
		return right-left+1;
	}
	int getHeight()const{
		return bottom-top+1;
	}
	int getSurface()const{
		return getWidth()*getHeight();
	}
	double getDensity()const{
		return nbPixels*1.0/getSurface();
	}
	double getHeightToWidthRatio()const{
		return getHeight()*1.0/getWidth();
	}
	double getLongality()const{
		return (getWidth()>getHeight())?(getHeight()*1.0/getWidth()):(getWidth()*1.0/getHeight());
	}
};

namespace cfilter{
/**
 * Abstract functor used to implement Component filters for selecting
 * a subset of Components that upholds a particular property.
 * For an example in usage look at ComponentSet::filterComponents.
 * All custom made components filters should inherit from
 * AbstractComponentFilter. The usage of filters would have been
 * implemented by lamda expressions in a language that supported it.
 */
class AbstractComponentFilter{
public:
	inline virtual bool operator()(Component&)=0;
};

/**
 * Component Filter Functor
 * Returns true if a Component is Valid.
 * A component is defined valid if it has
 * one or more pixels on the labeled image.
 */
class ValidComponent:public AbstractComponentFilter{
public:
	bool operator()(Component& c){
		return c.getNbPixels()>0;
	}
};

/**
 * Component Filter Functor
 * Returns true if a Component would fit in a
 * particular rectangle.
 */
class FitsIn:public AbstractComponentFilter{
	int width;int height;
public:
	/**
	 * Constructor of the functor defining the size of the
	 * rectangle in witch the Component should fit
	 * \param w the width of the rectangle
	 * \param h the height of the rectangle
	 */
	FitsIn(int w,int h){
		width=w;height=h;
	}
	bool operator()(Component& c){
		return c.getWidth()<=width && c.getHeight()<=height;
	}
};

}

///Smart pointer to a Component
typedef boost::shared_ptr<Component> ComponentPointer;


/**
 * A class that Extends Image<t_label> by adding a collection (std::map<t_label,ComponentPointer>)
 * of quadil::Component . The idea is to access the image as a set of classes of pixels
 */
class ComponentSet: public Image<t_label>{
	std::map<t_label,ComponentPointer> components;
	typedef std::map<t_label,ComponentPointer>::iterator ComponetIterator;
	void scanComponents(){
		int y,x;
		const t_label* curLine;
		t_label lastFound=0;
		for(y=0;y<height;y++){
			curLine=this->getRow(y);
			for(x=0;x<width;x++){
				if(curLine[x]){
					if(curLine[x]==lastFound){
						components.find(curLine[x])->second->addPixel(x,y);
					}else{
						lastFound=curLine[x];
						if(components.count(x)>0){
							components.find(curLine[x])->second->addPixel(x,y);
						}else{
							components.insert(std::pair<t_label,ComponentPointer>(curLine[x],boost::shared_ptr<Component>(new Component(*this,x,y,curLine[x]))));
						}
					}
				}
			}
		}
	}
	/**
	 * Clears all components in the ComponentSet. The Image data are not affected.
	 */
	void clearComponents(){
		for(ComponetIterator iter=components.begin();iter!=components.end();++iter){
			iter->second.reset();
		}
		components.clear();
	}
	/**
	 * Constructor that creates a ComponentSet without scanning the image
	 * to create the components. This constructor is intented to be used
	 * so that image data is set freely before updating components. Data
	 * of the image if random and Components are not scaned. In order for
	 * the image to be consistent ComponentSet::scanComponents or
	 * ComponentSet::scanComponents should be called explicitly after data
	 * are set.
	 * \param w the width of the ComponentSet
	 * \param h the height of the ComponentSet
	 */
	ComponentSet(int w,int h):Image<t_label>(w,h){}
public:
	/**
	 * Constructor that creates a ComponentSet comtaining the connected components
	 * of a Image<t_byte>
	 * \param i the input image. ComponentSet will have the same size as i
	 * \param eightNeighbors :if true , the connected components are calculated in the 8 neighborhood, else in the 4
	 */
	ComponentSet(const Image<t_byte> &i,bool eightNeighbors=true):Image<t_label>(i.width,i.height){
		if(eightNeighbors){
			i.labelConnectedComponents8(*this);
		}else{
			i.labelConnectedComponents4(*this);
		}
		scanComponents();
	}
	/**
	 * Force rescanning of the ComponentSet to update the Components in the image.
	 */
	void rescanComponents(){
		this->clearComponents();
		this->scanComponents();
	}
	/**
	 * Method that will remove from the ComponentSet all components
	 * for witch the functor returns false
	 * \param filter A functor taking a component as a paramater and returning a boolean
	 */
	void eraseComponents(cfilter::AbstractComponentFilter & filter){
		for(ComponetIterator iter=components.begin();iter!=components.end();++iter){
			if(!filter(*(iter->second))){
				iter->second->setAllPixelsToLabel(0);
			}
		}
		rescanComponents();
	}
	void getComponents(std::vector<ComponentPointer> &out,cfilter::AbstractComponentFilter & filter){
		for(ComponetIterator iter=components.begin();iter!=components.end();++iter){
			if(!filter(*(iter->second))){
				out.push_back(iter->second);
			}
		}
	}
	void getComponents(std::vector<ComponentPointer> &out){
		for(ComponetIterator iter=components.begin();iter!=components.end();++iter){
			out.push_back(iter->second);
		}
	}
	/**
	 * Method that will create a new ComponentSet from the current
	 * by copying all components that meet a filters criteria.
	 * \param filter A functor taking a component as a paramater and returning a boolean
	 * \return a new ComponentSet (with its own memory) containing only the components that passed the criteria
	 */
	ComponentSet copyComponents(cfilter::AbstractComponentFilter & filter){
		ComponentSet tmp(width,height);
		tmp.setAll(0);
		for(ComponetIterator iter=components.begin();iter!=components.end();++iter){
			if(!filter(*(iter->second))){
				iter->second->drawOnImage<t_label>(tmp,iter->first);
			}
		}
		tmp.scanComponents();
		return tmp;
	}
	/**
	 * Constructor that copies the contents of an Image<t_label> (or a
	 * ComponentSet) performing a deep Copy it.
	 */

	int countComponents(cfilter::AbstractComponentFilter & filter){
		int res=0;
		for(ComponetIterator iter=components.begin();iter!=components.end();++iter){
			if(filter(*(iter->second))){
				res++;
			}
		}
		return res;
	}
	ComponentSet(const Image<t_label> &i):Image<t_label>(i.deepCopy()){
		scanComponents();
	}
	/**
	 * Get a reference (boost::shared_ptr) to a component.
	 * caller must make shure that label exists in the image
	 * \param lab the label of the requested component.
	 * \return a boost::shared_ptr<Component> to requested component
	 */
	ComponentPointer getComponentSafe(t_label lab){
		return components.find(lab)->second;
	}
	~ComponentSet(){
		clearComponents();
	}
};

}
#endif /*COMPONENTS_HPP_*/
